package helper.scxml.strategy.sNodeUnit

import helper.base.A2LHM
import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.LHMHelper.A2LHMExpand.makeA2LHM
import helper.base.LHMHelper.A2LHMExpand.touch
import helper.base.LHMHelper.A3LHM
import helper.base.StringHelper.removeEmptyLine
import helper.base.string.ToLStringHelper.preTabAndTrim
import helper.base.string.ToStrHelper.IToStr
import helper.base.string.ToStrHelper.TabStringUnit
import helper.base.string.ToStrHelper.TabStringUnit.Companion.makeTabStringUnit
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config.ConfigBoolFun.aHeapNotContainsEventsFun
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.RangeTimedEvent.Companion.makeRangeTimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent
import helper.scxml.strategy.TotalStrategyHelper
import helper.scxml.strategy.TotalStrategyHelper.I2SNode
import helper.scxml.strategy.TotalStrategyHelper.ISNode
import helper.scxml.strategy.TotalStrategyHelper.KREWTuple
import helper.scxml.strategy.TotalStrategyHelper.KREWsTuple
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode.Companion.makeLeafSNode
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode.Expand.getTimeEventChoice
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode.Expand.getTimeEventChoiceWeightLHM
import helper.scxml.strategy.TotalStrategyHelper.REWTuple
import helper.scxml.strategy.TotalStrategyHelper.SNode
import helper.scxml.strategy.TotalStrategyHelper.SNode.Companion.makeSNode
import helper.scxml.strategy.sNodeUnit.SNodeUnitHelper.ISNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.A3E.toL2String
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.EnvRenSubT2SNodeUnitLHM.Companion.makeEnvRenNoRootSubT2SNodeUnitLHM
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.I2SNodeA3LHM.Companion.makeI2SNodeA3LHM
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.LeafSNodeProcessor.Companion.GetUseMany.makeAllManyLeafSNodeProcessor
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.NKREWTuple.Companion.getNKREWTuple
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.NKREWsTuple.Companion.makeNKREWsTuple
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.SFA2LHMExpand.toL2String
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.SUA2LHMExpand.toL2String
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.SubT2SNodeUnit.Companion.makeSubT2SNodeUnit

// T2SNodeUnitHelper 对象包含了用于策略执行的辅助类和方法
object T2SNodeUnitHelper {
    class TimedEventWeightTable : ArrayList<REWTuple>() {
        companion object {
            fun makeTimedEventWeightTable() = TimedEventWeightTable()
        }
    }

    // NKREWTuple 类封装了策略节点和其相关信息
    class NKREWTuple(val sNode: I2SNode?, val aKREWTuple: KREWTuple) {
        // 提供静态方法来构造 NKREWTuple 实例
        companion object {
            // 标准构造方法
            fun getNKREWTuple(n: I2SNode?, aKREWTuple: KREWTuple): NKREWTuple {
                return NKREWTuple(n, aKREWTuple)
            }
        }

        // 提供便捷的属性访问器
        val key: String
            get() = aKREWTuple.key
        val intRange: IntRange
            get() = aKREWTuple.intRange
        val event: String
            get() = aKREWTuple.event
        val weight: Double
            get() = aKREWTuple.weight

        operator fun component1() = sNode
        operator fun component2() = key
        operator fun component3() = intRange
        operator fun component4() = event
        operator fun component5() = weight
    }

    // NKREWsTuple 类封装了策略节点和其相关信息的集合
    class NKREWsTuple(val sNode: I2SNode?, val aKREsWTuple: KREWsTuple) {
        companion object {
            fun makeNKREWsTuple(
                sNode: I2SNode?,
                aKREsWTuple: KREWsTuple,
            ): NKREWsTuple {
                return NKREWsTuple(
                    sNode,
                    aKREsWTuple,
                )
            }
        }
    }

    // I2SNodeA3LHM 类扩展了 A3LHM，专门用于存储策略节点之间的关系
    class I2SNodeA3LHM : A3LHM<I2SNode?, String, I2SNode>() {
        companion object {
            fun makeI2SNodeA3LHM() = I2SNodeA3LHM()
        }

        private fun dg(sb: StringBuilder, n: I2SNode?, tsu: TabStringUnit, i: Int) {
            if (n is LeafSNode) {
                sb.append("_${n.toStr(tsu.getTabNum(i - 1)).trimStart()}")
                return
            }
            var newI = i
            if (n == null) {
                sb.append("${tsu.getTabNumStr(i)}null")
                newI++
            }

            this[n]?.map { (k, v) ->
                sb.append("\n${tsu.getTabNumStr(newI)}${k}")
                dg(sb, v, tsu, newI + 1)
            }
        }

        private fun toStr1(tabNum: Int): String {
            val sb = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)
            dg(sb, null, tsu, 0)
            return sb.toString()
        }

        override fun toStr(tabNum: Int): String {
            return toStr1(tabNum)
        }
    }

    // SubT2SNodeUnit 类表示子策略节点单元，封装了节点的功能和关系
    class SubT2SNodeUnit(
        val funLHM: A2LHM<I2SNode?, (Config) -> String>,
        val relationLHM: I2SNodeA3LHM,
    ) : IToStr {
        companion object {
            fun makeSubT2SNodeUnit(
                funLHM: A2LHM<I2SNode?, (Config) -> String> = A2LHM(),
                relationLHM: I2SNodeA3LHM = makeI2SNodeA3LHM(),
            ): SubT2SNodeUnit {
                return SubT2SNodeUnit(
                    funLHM,
                    relationLHM,
                )
            }
        }

        override fun toStr(tabNum: Int): String {
            val sb = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)
            sb.append("${tsu.tabNumStr0}${javaClass.simpleName}(")
            sb.append("funLHM: A2LHM<I2SNode?, (ZoneState) -> String>,relationLHM=\n")
            sb.append(relationLHM.toStr(tsu.tabNum1))
            sb.append("${tsu.tabNumStr0})\n")
            return sb.toString()
        }

        fun toL2String(): String {
            val sb = StringBuilder()
            sb.append("SubT2SNodeUnit(\n")
            sb.append("\tfunLHM=${funLHM.toL2String().preTabAndTrim()}\n")
            sb.append("\trelationLHM=${relationLHM.toL2String().preTabAndTrim()}\n")
            sb.append(")")
            return sb.toString()
        }

        // 私有方法用于获取下一个节点
        private fun getNextSNode(
            currentSNode: I2SNode?,
            pathElement: String,
        ): I2SNode? {
            return relationLHM[currentSNode, pathElement]
        }

        private fun traverseSNodes(
            initialSNode: I2SNode?,
            generateKey: (I2SNode?) -> String?,
        ): I2SNode? {
            var nowSNode: I2SNode? = initialSNode
            while (true) {
                val key = generateKey(nowSNode) ?: return null
                val nextSNode = getNextSNode(nowSNode, key) ?: return null

                if (nextSNode is LeafSNode) {
                    return nextSNode
                }
                nowSNode = nextSNode
            }
        }

        fun findSNode(fromPath: List<String>): I2SNode? {
            if (fromPath.isEmpty()) {
                return null
            }

            val pathIterator = fromPath.iterator()
            return traverseSNodes(null) { _ ->
                if (pathIterator.hasNext()) pathIterator.next() else null
            }
        }

        fun findLeafSNode(config: Config): LeafSNode? {
            return traverseSNodes(null) { node ->
                val function = funLHM[node] ?: return@traverseSNodes null
                function(config)
            } as? LeafSNode
        }

        fun addSNode(
            fSNode: I2SNode?,
            string: String,
            sNode: I2SNode,
        ) {
            relationLHM.add(fSNode, string, sNode)
        }

        fun addSNode(
            fSNode: I2SNode?,
            fKey: String,
            aZoneStateStringFun: (Config) -> String,
            aStringTableLHM: A2LHM<String, TimedEventWeightTable>,
        ) {
            val aSNode = makeSNode()
            relationLHM.add(fSNode, fKey, aSNode)
            funLHM.add(aSNode, aZoneStateStringFun)
            aStringTableLHM.forEach { (t, u) ->
                val leafSNode = makeLeafSNode()
                u.forEach {
                    leafSNode.eventDPLHM.add(makeRangeTimedEvent(it.intRange, it.event), it.weight)
                }
                relationLHM.add(aSNode, t, leafSNode)
            }
        }

        fun addSNode(
            fSNode: I2SNode?,
            fKey: String,
            lhm: A2LHM<(Config) -> Boolean, KREWTuple>,
            addedBoolFun: (Config, String) -> Boolean = { _, _ -> true },
        ) {
            val aSNode = makeSNode()
            relationLHM.add(fSNode, fKey, aSNode)
            funLHM.add(aSNode) aZSSFun@{ zs ->
                lhm.forEach { (t, u) ->
                    if (t(zs) && addedBoolFun(zs, u.event)) {
                        return@aZSSFun u.key
                    }
                }
                ""
            }
            lhm.values.forEach {
                addSNode(aSNode, it)
            }
        }

        //KREWsTuple
        fun addAddedBoolFunSNode(
            aSNode: I2SNode?,
            lhm: A2LHM<(Config) -> Boolean, KREWsTuple>,
            addedBoolFun: (Config, List<String>) -> Boolean = aHeapNotContainsEventsFun,
        ) {
            funLHM.add(aSNode) aZSSFun@{ zs ->
                lhm.forEach { (function, tuple) ->
                    if (function(zs) && addedBoolFun(zs, tuple.aREWs.map { it.event })) {
                        return@aZSSFun tuple.key
                    }
                }
                ""
            }
            lhm.values.forEach {
                addSNode(aSNode, it)
            }
        }

        fun addSNode(
            aNKREWTuple: NKREWTuple,
        ) {
            val (a0, a1, a2, a3, a4) = aNKREWTuple
            if (relationLHM[a0, a1] == null) {
                addSNode(a0, a1, makeLeafSNode())
            }
            val leafSNode = relationLHM[a0, a1]!! as LeafSNode
            leafSNode.eventDPLHM.add(makeRangeTimedEvent(a2, a3), a4)
        }

        fun addSNode(
            aNKREWsTuple: NKREWsTuple,
        ) {
            val leafSNode = makeLeafSNode()
            aNKREWsTuple.aKREsWTuple.aREWs.forEach { (a2, a3, a4) ->
                leafSNode.eventDPLHM.add(
                    makeRangeTimedEvent(a2, a3),
                    a4,
                )
            }
            addSNode(
                aNKREWsTuple.sNode,
                aNKREWsTuple.aKREsWTuple.key,
                leafSNode,
            )
        }

        fun addSNode(
            n: I2SNode?,
            aKREWsTuple: KREWsTuple,
        ) {
            addSNode(makeNKREWsTuple(n, aKREWsTuple))
        }

        fun addSNode(
            n: SNode?,
            aKREWTuple: KREWTuple,
        ) {
            addSNode(getNKREWTuple(n, aKREWTuple))
        }

        fun addSNode(
            vararg aNKREWTuple: NKREWTuple
        ) {
            aNKREWTuple.forEach {
                addSNode(it)
            }
        }

        fun addSNode(
            fSNode: SNode?,
            vararg aKREWTuple: KREWTuple,
        ) {
            aKREWTuple.forEach {
                addSNode(getNKREWTuple(fSNode, it))
            }
        }

        fun addSNode(
            vararg aKREWTuple: KREWTuple
        ) {
            addSNode(null, *aKREWTuple)
        }
    }

    // EnvRenSubT2SNodeUnitLHM 类封装了环境和重命名子节点单元的映射
    class EnvRenSubT2SNodeUnitLHM : A2LHM<String, SubT2SNodeUnit>() {
        companion object {
            fun makeEnvRenNoRootSubT2SNodeUnitLHM(
                env: SubT2SNodeUnit = makeSubT2SNodeUnit(),
                ren: SubT2SNodeUnit = makeSubT2SNodeUnit(),
            ): EnvRenSubT2SNodeUnitLHM {
                return EnvRenSubT2SNodeUnitLHM().apply {
                    this["env"] = env
                    this["ren"] = ren
                }
            }
        }
    }

    object SFA2LHMExpand {
        fun A2LHM<I2SNode?, (Config) -> String>.toL2String(): String {
            val sb = StringBuilder()
            sb.append("A2LHM(size=${size})=[\n")
            this.forEach { (t, u) ->
                sb.append("\t${t?.toL1String() ?: "null"}=${u}\n")
            }
            sb.append("]")
            return sb.toString()
        }
    }

    object A3E {
        fun A3LHM<I2SNode?, String, I2SNode>.toL2String(): String {
            val sb = StringBuilder()
            sb.append("A3LHM(size=${size})=[\n")
            this.touch { t1, t2, t3 ->
                sb.append("\t${t1}\n")
                sb.append("\t\t${t2}\n")
                sb.append("\t\t\t${t3.toL2String().preTabAndTrim(3)}\n")
            }
            sb.append("]")
            return sb.toString()
        }
    }

    // LeafSNodeProcessor 类处理叶子节点（LeafSNode），特别是时间事件选择逻辑
    class LeafSNodeProcessor(
        private val getTimeEventChoiceFun: (A2LHM<String, LeafSNode>) -> TimedEvent?,
        private val getTimeEventChoiceWeightLHMFun: (A2LHM<String, LeafSNode>) -> A2LHM<TimedEvent, Double>,
    ) {
        companion object {
            fun makeLeafSNodeProcessor(
                getTimeEventChoiceFun: (A2LHM<String, LeafSNode>) -> TimedEvent?,
                getTimeEventChoiceWeightLHMFun: (A2LHM<String, LeafSNode>) -> A2LHM<TimedEvent, Double>,
            ): LeafSNodeProcessor {
                return LeafSNodeProcessor(
                    getTimeEventChoiceFun,
                    getTimeEventChoiceWeightLHMFun,
                )
            }

            object GetUseOne {
                private fun getUseOneLeafSNodeProcessor(
                    theFun: (A2LHM<String, LeafSNode>) -> LeafSNode,
                ): LeafSNodeProcessor {
                    return makeLeafSNodeProcessor(
                        getTimeEventChoiceFun = a@{ lhm ->
                            theFun(lhm).getTimeEventChoice()
                        },
                        getTimeEventChoiceWeightLHMFun = a@{ lhm ->
                            theFun(lhm).getTimeEventChoiceWeightLHM()
                        },
                    )
                }

                private fun getUseFirstLeafSNodeProcessor(
                    strings: Array<String>,
                ): LeafSNodeProcessor {
                    return getUseOneLeafSNodeProcessor(theFun = a@{ lhm ->
                        strings.forEach {
                            if (lhm.containsKey(it)) {
                                return@a lhm[it]!!
                            }
                        }
                        throw Exception()
                    })
                }

                fun Array<String>.toUseFirstLeafSNodeProcessor(): LeafSNodeProcessor {
                    return getUseFirstLeafSNodeProcessor(this)
                }

                fun MutableSet<String>.toUseFirstLeafSNodeProcessor(): LeafSNodeProcessor {
                    return this.toTypedArray().toUseFirstLeafSNodeProcessor()
                }
            }

            object GetUseMany {
                private fun makeUseManyLeafSNodeProcessor(
                    theFun: (A2LHM<String, LeafSNode>) -> List<LeafSNode>,
                ): LeafSNodeProcessor {
                    return makeLeafSNodeProcessor(
                        getTimeEventChoiceFun = a@{ lhm ->
                            theFun(lhm).getTimeEventChoice()
                        },
                        getTimeEventChoiceWeightLHMFun = a@{ lhm ->
                            theFun(lhm).getTimeEventChoiceWeightLHM()
                        },
                    )
                }

                fun makeAllManyLeafSNodeProcessor(): LeafSNodeProcessor {
                    return makeUseManyLeafSNodeProcessor(
                        theFun = { lhm ->
                            lhm.values.toList()
                        },
                    )
                }
            }
        }

        fun toL2String(): String {
            val sb = StringBuilder()
            sb.append("LeafSNodeProcessor(\n")
            sb.append("\tgetTimeEventChoiceFun=${getTimeEventChoiceFun}\n")
            sb.append("\tgetTimeEventChoiceWeightLHMFun=${getTimeEventChoiceWeightLHMFun}\n")
            sb.append(")")
            return sb.toString()
        }

        fun getTimeEventChoice(
            stringLeafSNodeLHM: A2LHM<String, LeafSNode>,
        ): TimedEvent? {
            return getTimeEventChoiceFun(stringLeafSNodeLHM)
        }

        fun getTimeEventChoiceWeightLHM(
            stringLeafSNodeLHM: A2LHM<String, LeafSNode>,
        ): A2LHM<TimedEvent, Double> {
            return getTimeEventChoiceWeightLHMFun(stringLeafSNodeLHM)
        }
    }

    object SUA2LHMExpand {
        fun A2LHM<String, SubT2SNodeUnit>.toL2String(): String {
            val sb = StringBuilder()
            sb.append("A2LHM(size=${size})=[\n")
            this.forEach { (t, u) ->
                sb.append("\t${t}=${u.toL2String().preTabAndTrim()}\n")
            }
            sb.append("]")
            return sb.toString()
        }
    }

    open class T2SNodeUnit(
        val stringSubT2SNodeUnitLHM: A2LHM<String, SubT2SNodeUnit>,
        val leafSNodeProcessor: LeafSNodeProcessor,
    ) : ISNodeUnit, ISNode {
        companion object {
            fun makeT2SNodeUnit(
                stringSubT2SNodeUnitLHM: A2LHM<String, SubT2SNodeUnit>,
                leafSNodeProcessor: LeafSNodeProcessor,
            ): T2SNodeUnit {
                return T2SNodeUnit(
                    stringSubT2SNodeUnitLHM,
                    leafSNodeProcessor,
                )
            }
        }

        private fun getLeafSNodes(config: Config): A2LHM<String, LeafSNode> {
            val stringLeafSNodeLHM = A2LHM<String, LeafSNode>()
            stringSubT2SNodeUnitLHM.forEach { (a1, a2) ->
                val leafSNode = a2.findLeafSNode(config)
                leafSNode?.let {
                    stringLeafSNodeLHM.add(a1, it)
                }
            }
            return stringLeafSNodeLHM
        }

        fun getMTimeEventChoice(config: Config): TimedEvent? {
            return leafSNodeProcessor.getTimeEventChoice(getLeafSNodes(config))
        }

        fun getMTimeEventChoiceWeightLHM(config: Config): A2LHM<TimedEvent, Double> {
            return leafSNodeProcessor.getTimeEventChoiceWeightLHM(getLeafSNodes(config))
        }

        override fun toStr(tabNum: Int): String {
            val sb = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)
            sb.append("${tsu.tabNumStr0}${javaClass.simpleName}.stringSubT2SNodeUnitLHM=\n")
            stringSubT2SNodeUnitLHM.touch { t, u ->
                sb.append("${tsu.tabNumStr1}${t}_${u.toStr(tsu.tabNum1).trim()}\n")
            }
            return sb.toString().removeEmptyLine()
        }

        override fun toL2String(): String {
            val sb = StringBuilder()
            sb.append("T2SNodeUnit(\n")
            sb.append("\tstringSubT2SNodeUnitLHM=${stringSubT2SNodeUnitLHM.toL2String().preTabAndTrim()}\n")
            sb.append("\tleafSNodeProcessor=${leafSNodeProcessor.toL2String().preTabAndTrim()}\n")
            sb.append(")")
            return sb.toString()
        }

        override fun getMsTimeEventChoiceList(config: Config): ArrayList<TimedEvent> {
            val res = arrayListOf<TimedEvent>()
            getMTimeEventChoice(config)?.let {
                res.add(it)
            }
            return res
        }

        override fun getMsTimeEventChoiceWeightLHM(config: Config): A2LHM<List<TimedEvent>, Double> {
            val a2LHM = makeA2LHM<List<TimedEvent>, Double>()
            getMTimeEventChoiceWeightLHM(config).forEach { (t, u) ->
                a2LHM.add(arrayListOf(t), u)
            }
            return a2LHM
        }
    }

    // ERT2SNodeUnit 类表示特定于环境和重命名的策略节点单元
    class ERT2SNodeUnit(
        envSubT2SNodeUnit: SubT2SNodeUnit = makeSubT2SNodeUnit(),
        renSubT2SNodeUnit: SubT2SNodeUnit = makeSubT2SNodeUnit(),
    ) : T2SNodeUnit(
        makeEnvRenNoRootSubT2SNodeUnitLHM(
            envSubT2SNodeUnit,
            renSubT2SNodeUnit,
        ),
        makeAllManyLeafSNodeProcessor(),
    ) {
        companion object {
            fun makeERT2SNodeUnit(
                env: SubT2SNodeUnit = makeSubT2SNodeUnit(),
                ren: SubT2SNodeUnit = makeSubT2SNodeUnit(),
            ): ERT2SNodeUnit {
                return ERT2SNodeUnit(
                    env,
                    ren,
                )
            }

            fun makeL0ERT2SNodeUnit(
                env: SubT2SNodeUnit = makeSubT2SNodeUnit(),
                ren: SubT2SNodeUnit = makeSubT2SNodeUnit(),
            ): ERT2SNodeUnit {
                return makeERT2SNodeUnit(
                    env,
                    ren,
                ).also {
                    it.stringSubT2SNodeUnitLHM.values.forEach {
                        it.funLHM.add(
                            null,
                            Config::getState0String,
                        )
                    }
                }
            }
        }

        val env: SubT2SNodeUnit
            get() {
                return stringSubT2SNodeUnitLHM["env"]!!
            }

        val ren: SubT2SNodeUnit
            get() {
                return stringSubT2SNodeUnitLHM["ren"]!!
            }
    }
}